/*
 * bitwise.cpp
 *
 *  Created on: May 23, 2011
 *      Author: Manslaughter
 */

#include "BigInt.h"
#include <string.h>
using namespace APIAL;

/*
 * These ops need to normalize the chunks before we act on them.
 *
 * All these ops assume that 0 is represented by bigint.size == 0
 *
 */

/************************************************************************/
/* Bitwise AND op
	Perform (result = left & right).
	Aliases are ok.
	Result resized as necessary.
	result.positive = left.positive == right.positive (like multiplication)
/************************************************************************/
void APIAL::And(BigInt *left, BigInt *right, BigInt *result) {
    // An AND between two values is just the value itself
    if(left == right) {
        BigInt::copyLeft(left, result);
        return;
    }

	// Normalize both operands, as this can affect the size
	BigInt::normalize(*left);
	BigInt::normalize(*right);

    // For AND case we don't worry about the trailing bits    
    // Set the size to the smallest operand size.
    int minSize = (left->size < right->size) ? left->size : right->size;
    
    // Resize result if needed (aliases will never reach this code)
    if(result->capacity < minSize) {
        result->resize(minSize, false);
	}

	// Perform AND (aliases are ok)
	#pragma omp parallel for shared(left, right, result, minSize)
	for(int i = 0; i < minSize; i++) {
		result->chunks[i] = left->chunks[i] & right->chunks[i];
	}

	// Determine result size (requires trimming) and sign
	result->size = 0;
	result->positive = left->positive == right->positive;

	// TODO: Optimize trim
	// Trim zeros
	for (int i = minSize-1; i >= 0; i--) {
		if (result->chunks[i] != 0) {
			result->size = i+1;
			break;
		}
	}
}

/************************************************************************/
/* bitwise OR op
	Perform (result = left | right).
	Aliases are ok.
	Result resized as necessary.
	result.positive = left.positive == right.positive (like multiplication)
/************************************************************************/
void APIAL::Or(BigInt *left, BigInt *right, BigInt *result) {
    // If we OR a value with itself, we get back the same value
    if(left == right) {
        BigInt::copyLeft(left, result);
        return;
    }

	// Normalize both operands, as this can affect the size
	BigInt::normalize(*left);
	BigInt::normalize(*right);

    // Get the size of the biggest sized element
	BigInt *minOp = left;
	BigInt *maxOp = right;
	if (left->size > right->size) {
		minOp = right;
		maxOp = left;
	}

	// Check for result alias
	bool resultAlias = left == result || right == result;

    // Resize result if needed
    if(result->capacity < maxOp->size)
        result->resize(maxOp->size, resultAlias);

	// Perform the OR
    #pragma omp parallel shared(left, right, result, minOp, maxOp)
    {
		// OR left and right
        #pragma omp for nowait
        for(int i = 0; i < minOp->size; i++) {
			result->chunks[i] = left->chunks[i] | right->chunks[i];
        }

		// Anything ORed with 0 = itself
		#pragma omp for nowait
		for (int i = minOp->size; i < maxOp->size; i++) {
			result->chunks[i] = maxOp->chunks[i];
		}
    }

	// Set result size and sign
	result->size = maxOp->size;
	result->positive = left->positive == right->positive;
}

/************************************************************************/
/* bitwise XOR op
	Perform (result = left ^ right).
	Aliases are ok.
	Result resized as necessary.
	result.positive = left.positive == right.positive (like multiplication)
/************************************************************************/
void APIAL::Xor(BigInt *left, BigInt *right, BigInt *result) {
    // An XOR between two values should just zero out the result
    if(left == right) {
        result->size = 0;
        return;
    }

	// Normalize both operands, as this can affect the size
    BigInt::normalize(*left);
    BigInt::normalize(*right);

	// Get the size of the biggest sized element
	BigInt *minOp = left;
	BigInt *maxOp = right;
	if (left->size > right->size) {
		minOp = right;
		maxOp = left;
	}

	// Check for result alias
	bool resultAlias = left == result || right == result;

	// Resize result if needed
	if(result->capacity < maxOp->size)
		result->resize(maxOp->size, resultAlias);

	// Perform the XOR
	#pragma omp parallel shared(left, right, result, minOp, maxOp)
	{
		// XOR left and right
		#pragma omp for nowait
		for(int i = 0; i < minOp->size; i++) {
			result->chunks[i] = left->chunks[i] ^ right->chunks[i];
		}

		// Anything XORed with 0 = itself
		#pragma omp for nowait
		for (int i = minOp->size; i < maxOp->size; i++) {
			result->chunks[i] = maxOp->chunks[i];
		}
	}

	// Determine result size (requires trimming) and sign
	int maxSize = maxOp->size;
	result->size = 0;
	result->positive = left->positive == right->positive;

	// TODO: Optimize trim
	// Trim zeros
	for (int i = maxSize-1; i >= 0; i--) {
		if (result->chunks[i] != 0) {
			result->size = i+1;
			break;
		}
	}
}

/************************************************************************/
/* bitwise NOT op
	Perform (result = left ^ right).
	Aliases are ok.
	Result resized as necessary.
	result.positive = left.positive 
/************************************************************************/
void APIAL::Not(BigInt *left, BigInt *result) {
	// Normalize the operand to allow bitwise operations
    BigInt::normalize(*left);

    // Resize result if needed (aliases will never reach this code)
    if(result->capacity < left->size)
        result->resize(left->size, false);

    // Create a bitmask for leading chunk 
    BigInt::chunkType mscMask = 0;
    BigInt::chunkType msc = left->chunks[left->size-1];
    while (msc != 0) {
        msc = msc >> 1;
        mscMask = (mscMask << 1) | 1;
    }

    //mscMask = mscMask >> 1;
	// Perform the NOT on all chunks except most significant chunk
    #pragma omp parallel for shared(left, result)
	for(int i = 0; i < left->size - 1; i++) {
		result->chunks[i] = (~left->chunks[i])&left->nonCarryBitsMask;
    }
    
    // Mask the most significant chunk
    result->chunks[left->size - 1] = (~left->chunks[left->size -1]) & mscMask;

	// Determine result size (requires trimming) and sign
	int maxSize = left->size;
	result->size = 0;
	result->positive = left->positive;

	// TODO: Optimize trim
	// Trim zeros
	for (int i = maxSize-1; i >= 0; i--) {
		if (result->chunks[i] != 0) {
			result->size = i+1;
			break;
		}
	}
}

/************************************************************************/
/* copyLeft helper function
    This function will take left chunks and copy it to result chunks
    and modify size and capacity accordingly.
/************************************************************************/
void BigInt::copyLeft(BigInt *left, BigInt *result) {
	// If alias, do nothing
	if (left == result)
		return;

	// Resize result array
	if (result->capacity < left->size)
		result->resize(left->size, false);

	// Copy result
	result->size = left->size;
	result->positive = left->positive;
	memcpy(result->chunks, left->chunks, left->size*sizeof(BigInt::chunkType));
}
